package game.model;

import com.sghd.common.utils.ManagedException;
import com.sghd.common.utils.math.RandomUtils;
import game.rule.Rule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * \* Created: fank
 * \* Date: 2018/5/18
 * \* Time: 14:38
 * \* @desc: 棋盘
 */
public class ChessBoard {
    private Logger logger = LoggerFactory.getLogger(getClass());

    /** 列*/
    private int col;
    /** 行*/
    private int row;
    /** 棋格*/
    private Grid[] grids;
    /** 被消除的元素*/
    private Set<Integer> eliminated = new HashSet<>();
    /** 获取结果次数*/
    private int run;

    public static ChessBoard valueOf(int row, int col) {
        ChessBoard chessBoard = new ChessBoard();
        chessBoard.col = col;
        chessBoard.row = row;
        chessBoard.grids = new Grid[row * col];
        for (int i = 0; i < chessBoard.grids.length; i++) {
            chessBoard.grids[i] = new Grid(i);
        }
        return chessBoard;
    }

    /**
     * 开始游戏
     * */
    public GameResult begin(boolean free, int roomId, long base, AtomicLong refund, int[] refundCtrl) {
        GameResult gameResult = new GameResult(free ? 0 : base * Rule.getSize(), free);
        this.run = 0;
        random(true, gameResult, free, roomId, base, refund, refundCtrl);
        return gameResult;
    }

    /**
     * 随机棋盘
     * */
    public void random(boolean gen, GameResult gameResult, boolean free, int roomId, long base,
                       AtomicLong refund, int[] refundCtrl) {
        if (gen) {//重新随机棋子
            genreter(gameResult, free, roomId);//生成
        } else {
            //重新填充(下落的棋子)
            refill(gameResult, free, roomId);
        }
        while (true) {//消除-下落-消除-下落，直到，没有可消除的
            Result result = Rule.elimite(grids, eliminated, base);//寻找连线并消除
            result.print();
            print("消除后的棋盘");
            boolean verify = verifyResult(refund, refundCtrl, result, base);// 验证结果是否满足条件
            if (verify) {
                //上一轮的数据
                if (!eliminated.isEmpty()) {
                    if (!gen) {
                        eliminated.forEach(leq -> {//还原被消除的元素
                            grids[leq].setMark(false);
                        });
                    }
                    eliminated.clear();
                }
                System.out.println("分数 受控制：" + gen);
                random(gen, gameResult, free, roomId, base, refund, refundCtrl);
                return;
            }
            if (result.isElimit()) {
                gameResult.add(result, result.getTotalFen());//添加一次下落记录
                refund.addAndGet(-result.getTotalFen());//更新反分池
                downAndFill(result, free, roomId);//下落并填充
                gen = false;
            } else {
                break;
            }
        }
    }

    /**
     * 验证结果是不是不满足，条件
     * @param refund 反分池
     * @param refundCtrl 反分池控制
     * @param result 游戏结果
     * @param base 单线押注金额
     * @return 是否重新随机或者填充棋盘
     */
    private boolean verifyResult(AtomicLong refund, int[] refundCtrl, Result result, long base) {
        //TODO
//        if (true) {
//            return false;
//        }
        if (run >= 20) {//递归调用20次重复填充，得不到想要的结果 ，跳出循环，防止堆溢出
            return false;
        }
        long fan = refund.get();
        //反分池上下限反分值
        long mini = refundCtrl[0] * base * Rule.getSize(), max = refundCtrl[1] * base * Rule.getSize();
        //反分池在正常区间内，不作控分处理
        if (fan >= mini && fan < max) {
            return false;
        }
        //反分池，斥资
        if (fan < mini) {
            //只能中小余1倍的奖励
            if (result.getTotalFen() > base * Rule.getSize()) {
                return true;
            }
            return false;
        }
        if (fan >= max) {//反分池，堆积
            //一定会中奖
            if (result.getTotalFen() == 0) {
                return true;
            }
            return false;
        }
        throw new ManagedException(-1, "控分发生未知错误");
    }

    /**
     * 生成棋盘
     * */
    private void genreter(GameResult gameResult, boolean free, int roomId) {
        List<Integer> seque = new ArrayList<>();
        for (int i = 0; i < grids.length; i++) {
            Grid grid = grids[i];
            grid.fill(ChessType.getChess(free, roomId));
            seque.add(grid.getChess().ordinal());
        }
        gameResult.setSequen(seque);//新生成的棋盘排列
        print("生成后的棋盘");
    }

    public static void main(String[] args) {
        ChessBoard board = valueOf(3, 5);
        board.genreter(null, false, 0);
        System.out.println();
        board.elimite();
        System.out.println();
        board.downAndFill(new Result(), false, 0);


    }

    /**
     * 消除
     */
    private void elimite() {
        for (int i = 0; i < 6; i++) {
            int random = RandomUtils.nextInt(grids.length);
            Grid grid = grids[random];
            grid.remove();
            eliminated.add(random);
        }
        print("消除后的棋盘");
    }

    /**
     * 下落填充
     * @return 能否继续消除
     */
    private boolean downAndFill(Result result, boolean free, int roomId) {
        //下落
        List<Integer> sorted = new ArrayList<>(eliminated);
        Collections.sort(sorted);
        for (int i = sorted.size(); i > 0; i--) {
            int index = sorted.get(i - 1);
            Grid grid = grids[index];
            if (grid.isEmpty()) {
                ChessType top = getTop(index);
                grid.fill(top);
            }
        }
        eliminated.clear();
        print("下落后的棋盘");
        //填充
        fill(result, free, roomId);
        return true;
    }

    /**
     * 填充
     */
    private void fill(Result result, boolean free, int roomId) {
        //填充
        List<Integer> fills = new ArrayList<>();
        /** 每一列待填充数据的索引*/
        List<Integer> col2Indes = new ArrayList<>();
        Arrays.stream(grids).collect(Collectors.groupingBy(o -> o.getIndex() / row)).forEach((col, grids) -> {
            grids.stream().filter(grid -> grid.isEmpty()).sorted((o1, o2) -> -1).forEach(grid -> {
                ChessType chess = ChessType.getChess(free, roomId);
                grid.fill(chess);
                fills.add(chess.ordinal());
                col2Indes.add(grid.getIndex());
            });
            result.addFillsGrid(col, new ArrayList<>(col2Indes));//记录填充的索引，得分受控时重复填充使用
            col2Indes.clear();
            result.addFill(col, new ArrayList<>(fills));//元素消除后，每列填充的元素
            fills.clear();
        });
        print("填充后的棋盘");
        logger.debug("填充的数据：{}", result.getFills());
    }

    /**
     * 重新填充元素
     * */
    private void refill(GameResult gameResult, boolean free, int roomId) {
        run++;
        Result lastResult = gameResult.getLastResult();
        Map<Integer, List<Integer>> lastFills = lastResult.getFills();
        List<Integer> fills = new ArrayList<>();
        lastResult.getFillsGrid().forEach((col, indexs) -> {
            indexs.forEach(i -> {
                Grid grid = grids[i];
                ChessType chess = ChessType.getChess(free, roomId);
                grid.fill(chess);
                fills.add(chess.ordinal());
            });
            lastFills.put(col, new ArrayList<>(fills));
            fills.clear();
        });
        print("重新填充后的棋盘");
        logger.debug("填充的数据：{}", lastResult.getFills());
    }

    private ChessType getTop(int currentIndex) {
        //是否第一行
        boolean firstRow = currentIndex % row == 0;
        if (firstRow) {
            return null;
        } else {
            ChessType result = null;
            int topIndex = currentIndex - 1;
            Grid chess = grids[topIndex];
            if (chess.isEmpty()) {
                result = getTop(topIndex);
            } else {
                result = chess.remove();
            }
            chess.fill(getTop(topIndex));
            return result;
        }
    }

    /**
     * 打印
     */
    public void print(String msg) {
        if (!logger.isDebugEnabled()) {
            return;
        }
        System.out.println(msg);
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                int index = i + j * row;
                try {
                    Grid grid = grids[index];
                    ChessType chess = grid.getChess();
                    if (grid.isEmpty()) {
                        System.out.print("null" + "\t");
                    } else {
                        System.out.print(chess + "\t");
                    }
                } catch (Exception e) {
                    System.out.print("null" + "\t");
                }
            }
            System.out.println();
        }
        System.out.println();
    }

    public int getCol() {
        return col;
    }

    public void setCol(int col) {
        this.col = col;
    }

    public int getRow() {
        return row;
    }

    public void setRow(int row) {
        this.row = row;
    }

    public Grid[] getGrids() {
        return grids;
    }

    public void setGrids(Grid[] grids) {
        this.grids = grids;
    }

}
